package ms.jasim.framework.reaction;

import java.io.File;
import java.io.FileNotFoundException;

import ms.jasim.framework.ICustomEventReaction;
import ms.jasim.framework.IJasimAppContext;
import ms.jasim.model.Actor;
import ms.jasim.model.Capability;
import ms.jasim.model.Decomposition;
import ms.jasim.model.Dependency;
import ms.jasim.model.Goal;
import ms.jasim.model.GoalRequest;
import ms.jasim.model.JasimModel;
import ms.jasim.model.event.CustomReaction;
import ms.jasim.model.event.Event;

import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.util.EcoreUtil;

public class InjectModelReaction implements ICustomEventReaction {

	private JasimModel newModel;
	private CustomReaction reaction;
	private Event event;
	private IJasimAppContext context;

	@Override
	public boolean react(IJasimAppContext context, JasimModel model, Event event, CustomReaction reaction) throws Exception {
		this.reaction = reaction;
		this.event = event;
		this.context = context;
		loadSourceModel(context, model, event);

		String objType = (String) reaction.getParamValue("ObjectType");
		if ("actor".equalsIgnoreCase(objType))
			injectActor(model);
		else if ("goal".equalsIgnoreCase(objType))
			injectGoal(model);
		else if ("capability".equalsIgnoreCase(objType))
			injectCapability(model);
			
		return false;
	}

	private void injectCapability(JasimModel model) {
		String goalName = (String) event.getParameterValue(context, (String) getParameter("GoalNameParameter", "Goal"));
		String actorName = (String) event.getParameterValue(context, (String) getParameter("ActorNameParameter", "Actor"));
		Actor actor = model.getActors().get(actorName);
		Goal goal = model.getGoals().get(goalName);
		
		if (actor != null && goal != null) {
			Actor newActor = newModel.getActors().get(actor.getName());
			Goal newGoal = newModel.getGoals().get(goal.getName());
			
			
			if (newActor == null || newGoal == null || goal == null)
				return;
			
			Capability newCap = newActor.findCapability(newGoal);
			if (newCap != null) {
				Capability cap = (Capability) EcoreUtil.copy(newCap);
				cap.setGoal(goal);
				actor.getCapability().add(cap);
			}
		}
	}

	private void injectGoal(JasimModel model) {
		String goalName = (String) event.getParameterValue(context, (String)getParameter("GoalNameParameter", "GoalName"));
		Goal goal = newModel.getGoals().get(goalName);
		if (goal != null && model.getGoals().get(goalName)== null) {
			Goal newGoal = (Goal) EcoreUtil.copy(goal);
			// correct decomposition references
			for(int i = newGoal.getDecomposition().size() - 1; i>=0; i--) {
				Decomposition de = newGoal.getDecomposition().get(i);
				boolean success = true;
				for (int j = 0; j <= de.getSubGoals().size() && success; j++) {
					Goal subGoal= de.getSubGoals().get(j);
					Goal g = model.getGoals().get(subGoal.getName());
					if (g != null)
						de.getSubGoals().set(j, g);
					else
						success = false;
				}
				if (!success)
					newGoal.getDecomposition().remove(i);
			}
			model.getGoals().getGoal().add(newGoal);
		}
		
	}

	private void injectActor(JasimModel model) {
		String actorName = (String) event.getParameterValue(context, (String)getParameter("ActorNameParameter", "ActorName"));
		Actor actor = newModel.getActors().get(actorName);
		if (actor != null && model.getActors().get(actorName) == null) {
			Actor newActor = (Actor) EcoreUtil.copy(actor);
			// redirect goal request, remove invalid reference
			for(int i = newActor.getRequests().size() - 1; i >= 0; i--) {
				GoalRequest request = newActor.getRequests().get(i);
				if (request.getGoal() != null)
					request.setGoal(model.getGoals().get(request.getGoal().getName()));
				if (request.getGoal() == null)
					newActor.getRequests().remove(i);
			}
			// redirect capability, remove invalid reference
			for(int i = newActor.getCapability().size() - 1; i >= 0; i--) {
				Capability cap = newActor.getCapability().get(i);
				if (!redirectCapability(model, cap))
					newActor.getCapability().remove(i);
			}
			// redirect dependences
			for (int i = newActor.getDependency().size() - 1; i >= 0; i--) {
				Dependency dependency = newActor.getDependency().get(i);
				
				EList<Actor> targetActors = dependency.getTargetActor();
				for(int j = targetActors.size() - 1; j >= 0; j--) {
					Actor newTarget = model.getActors().get(targetActors.get(j).getName());
					if (newTarget != null) 
						targetActors.set(j, newTarget);
					else
						targetActors.remove(j);
				}
				
				EList<Goal> targetGoals = dependency.getGoal();
				for(int j = targetGoals.size() - 1; j >= 0; j--) {
					Goal newTarget = model.getGoals().get(targetGoals.get(j).getName());
					if (newTarget != null) 
						targetGoals.set(j, newTarget);
					else
						targetGoals.remove(j);
				}
			}
			model.getActors().getActor().add(newActor);
		}
	}

	private boolean redirectCapability(JasimModel model, Capability cap) {
		if (cap.getGoal() != null)
			cap.setGoal(model.getGoals().get(cap.getGoal().getName()));
		return cap.getGoal() != null;
	}
	
	private void loadSourceModel(IJasimAppContext context, JasimModel model,
			Event event) throws Exception {
		//URI uri = EcoreUtil.getURI(model);
		//File modelFile = new File(uri.toFileString());
		String newFile = (String)event.getParameterValue(context, (String)getParameter("SourceFileParameter", "SourceFile"));
		if (! new File(newFile).exists()) 
			throw new FileNotFoundException(newFile);
		
		ResourceSet set= new ResourceSetImpl();
		Resource resource= set.getResource(URI.createFileURI(newFile), true);
		EObject rootObj = resource.getContents().get(0);
		
		if (!(rootObj instanceof JasimModel))
			throw new Exception("");
		
		newModel = (JasimModel) rootObj;
	}

	@SuppressWarnings("unused")
	private GoalRequest findRequest(Actor newActor, Goal newGoal) {
		for (GoalRequest req: newActor.getRequests())
			if (req.getGoal() == newGoal)
				return req;
		return null;
	}

	private Object getParameter(String paramName, Object defaultValue) {
		Object result = reaction.getParamValue(paramName);
		return result != null? result: defaultValue;
	}
}
